
/*
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.

    To request the provided software under a different license you can contact us at
    support@xoronos.com
*/

#include "xrn_arithmetic_backend.h"

static char base64_table[] =
    { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
    'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
    '+', '/'
};
static int mod_table[] = { 0, 2, 1 };

void base64_encode( char *data, int input_length, char *encoded_data
 )
{
    int output_length, i, j;
    uint32_t octet_a, octet_b, octet_c, triple;

    output_length = 4 * ( ( input_length + 2 ) / 3 );
    i = 0;
    j = 0;
    while ( i < input_length ) {
        octet_a = i < input_length ? ( unsigned char ) data[i++] : 0;
        octet_b = i < input_length ? ( unsigned char ) data[i++] : 0;
        octet_c = i < input_length ? ( unsigned char ) data[i++] : 0;
        triple = ( octet_a << 0x10 ) + ( octet_b << 0x08 ) + octet_c;

        encoded_data[j++] = base64_table[( triple >> 3 * 6 ) & 0x3F];
        encoded_data[j++] = base64_table[( triple >> 2 * 6 ) & 0x3F];
        encoded_data[j++] = base64_table[( triple >> 1 * 6 ) & 0x3F];
        encoded_data[j++] = base64_table[( triple >> 0 * 6 ) & 0x3F];
    }

    for ( i = 0; i < mod_table[input_length % 3]; i++ ) {
        encoded_data[output_length - 1 - i] = '=';
    }

    encoded_data[output_length] = '\0';

}

int base64_decode( char *data, int input_length, char *decoded_data
 )
{
    int output_length, i, j;
    int decoding_table[256];
    uint32_t sextet_a, sextet_b, sextet_c, sextet_d, triple;

    if ( input_length % 4 != 0 ) {
        XPRINT_ERR( 1988, XERR_IO_MSG );
        return XERR_IO;
    }

    output_length = input_length / 4 * 3;
    if ( data[input_length - 1] == '=' ) {
        ( output_length )--;
    }

    if ( data[input_length - 2] == '=' ) {
        ( output_length )--;
    }

    for ( i = 0; i < 64; i++ ) {
        decoding_table[( unsigned char ) base64_table[i]] = i;
    }

    i = 0;
    j = 0;
    while ( i < input_length ) {
        sextet_a = ( uint32_t ) ( data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]] );
        sextet_b = ( uint32_t ) ( data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]] );
        sextet_c = ( uint32_t ) ( data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]] );
        sextet_d = ( uint32_t ) ( data[i] == '=' ? 0 & i++ : decoding_table[( int ) data[i++]] );
        triple = ( sextet_a << 3 * 6 )
            + ( sextet_b << 2 * 6 ) + ( sextet_c << 1 * 6 ) + ( sextet_d << 0 * 6 );

        if ( j < output_length )
            decoded_data[j++] = ( char ) ( ( triple >> 2 * 8 ) & 0xFF );
        if ( j < output_length )
            decoded_data[j++] = ( char ) ( ( triple >> 1 * 8 ) & 0xFF );
        if ( j < output_length )
            decoded_data[j++] = ( char ) ( ( triple >> 0 * 8 ) & 0xFF );
    }

    return XSUCCESS;
}

void prettify_b64( char str[]
 )
{

    int i, str_len;
    int first_plus_index;
    int first_slash_index;
    int last_plus_index;
    int last_slash_index;

    first_plus_index = -1;
    first_slash_index = -1;
    last_plus_index = -1;
    last_slash_index = -1;

    i = 0;

    if ( XNBITS == 128 ) {
        str_len = 22 + 1;
    } else if ( XNBITS == 256 ) {
        str_len = 43 + 1;
    }

    // it works only for 128 and 256 bits

    while ( ( str[i] != '=' ) && ( i < str_len ) ) {

        if ( str[i] == '+' ) {

            if ( first_plus_index == -1 ) {
                first_plus_index = i;
            } else {
                str[i] = base64_table[last_plus_index];
            }

            last_plus_index = i;
        }

        if ( str[i] == '/' ) {

            if ( first_slash_index == -1 ) {
                first_slash_index = i;
            } else {
                str[i] = base64_table[last_slash_index];
            }

            last_slash_index = i;
        }

        i++;
    }

    str[str_len] = '\0';

    // if there are no special characters
    if ( first_plus_index == first_slash_index ) {

        str[i] = base64_table[i];

    } else if ( ( first_plus_index >= 0 ) && ( first_slash_index <= -1 ) ) {

        str[i] = base64_table[last_plus_index];
        str[first_plus_index] = ( char ) base64_table[61];

    } else if ( ( first_slash_index >= 0 ) && ( first_plus_index <= -1 ) ) {

        str[i] = base64_table[last_slash_index];
        str[first_slash_index] = base64_table[60];

    } else if ( first_plus_index < first_slash_index ) {

        str[i] = base64_table[last_plus_index];
        str[first_plus_index] = base64_table[last_slash_index];
        str[first_slash_index] = base64_table[59];

    } else {

        str[i] = base64_table[last_slash_index];
        str[first_slash_index] = base64_table[last_plus_index];
        str[first_plus_index] = base64_table[58];

    }

    return;

}

void deprettify_b64( char str[]
 )
{

    int i, len, str_len, start_index, index, end_condition, end, switch_char;
    char end_char;
    int decoding_table[256];
    int index_vector[44];

    index = 0;

    for ( i = 0; i < 64; i++ ) {
        decoding_table[( unsigned char ) base64_table[i]] = i;
    } len = ( int ) strnlen( str, XRN_MAX_STR_SIZE ) - 1;

    start_index = decoding_table[( int ) str[len]];
    end_char = 0;

    if ( XNBITS == 128 ) {
        str_len = 22;
        str[len] = '=';
        str[len + 1] = '=';
        str[len + 2] = '\0';
    } else if ( XNBITS == 256 ) {
        str_len = 44;
        str[len] = '=';
        str[len + 1] = '\0';
    }

    // there are no special characters
    if ( start_index == len ) {

        return;

    }

    // get end condition

    end_condition = 0;

    index_vector[0] = start_index;
    if ( start_index < str_len ) {
        index = ( int ) decoding_table[( int ) str[start_index]];
        end_char = base64_table[index];
        index_vector[1] = index;
    }

    i = 0;
    end = 0;
    while ( ( end != 1 ) && ( i < str_len ) ) {

        if ( index < str_len ) {
            index = decoding_table[( int ) str[index]];
            end_char = base64_table[index];

            index_vector[i + 2] = index;
        }

        if ( end_char == base64_table[61] ) {
            end_condition = 1;
        }

        if ( end_char == base64_table[60] ) {
            end_condition = 2;
        }

        if ( end_char == base64_table[59] ) {
            end_condition = 3;
        }

        if ( end_char == base64_table[58] ) {
            end_condition = 4;
            str[start_index] = '/';
        }

        i++;

        end = ( ( end_char == base64_table[61] ) ||
                ( end_char == base64_table[60] ) ||
                ( end_char == base64_table[59] ) || ( end_char == base64_table[58] ) );

    }

    index_vector[i + 1] = 64;

    i = 0;

    while ( index_vector[i] != 64 ) {
        i++;
    }

    i = 0;
    switch_char = 0;

    while ( index_vector[i] != 64 ) {

        if ( end_condition == 1 ) {
            str[index_vector[i]] = '+';
        }

        if ( end_condition == 2 ) {
            str[index_vector[i]] = '/';
        }

        if ( end_condition == 3 ) {
            if ( switch_char == 0 ) {
                str[index_vector[i]] = '+';
            } else {
                str[index_vector[i]] = '/';
            }

            if ( ( index_vector[i + 1] > index_vector[i] ) && ( switch_char == 0 ) ) {
                switch_char = 1;
            }

        }

        if ( end_condition == 4 ) {
            if ( switch_char == 0 ) {
                str[index_vector[i]] = '/';
            } else {
                str[index_vector[i]] = '+';
            }

            if ( ( index_vector[i + 1] > index_vector[i] ) && ( switch_char == 0 ) ) {
                switch_char = 1;
            }

        }

        i++;
    }

    return;

}

void print_xnum_t_hex( xnum_t xnum
 )
{
    char str[1024];

    snprintf( str, 1024, "%s", "" );
    xrn_bin_to_hex( xnum, str );
    printf( "%s", str );
}

void print_xnum_b64( xnum_t xnum
 )
{
    char str[1024];

    snprintf( str, 1024, "%s", "" );
    xrn_bin_to_base64( xnum, str );
    printf( "%s", str );
}

void print_xnum_t( xnum_t xnum
 )
{
    int i, j;

    XINT_TYPE tmp;
    char str[1024];

    snprintf( str, 1024, "%s", "" );
    for ( i = 0; i < XNBITS_NUM; i++ ) {
        tmp = xnum.xnum[i];
        for ( j = 0; j < XBASE_NUM; j++ ) {
            if ( ( tmp >> j ) & ( ( XINT_TYPE ) 1 ) )
                strncat( str, "1", 1023 );

            else
                strncat( str, "0", 1023 );
        }

    }

    printf( "%s", str );
}

void print_xmat_t( xmat_t xmat
 )
{
    int i;

    for ( i = 0; i < ( sizeof( xnum_t ) * 8 ); i++ ) {
        print_xnum_t( xmat.xmat[i] );
        printf( "\n" );
    }

}

